home *** CD-ROM | disk | FTP | other *** search
- Subject: REPOST v22i052: NN Newsreader, release 6.4, Part15/21
- Newsgroups: comp.sources.unix
- Approved: rsalz@uunet.UU.NET
- X-Checksum-Snefru: ff1ae712 ab8ee557 42c00e18 11ad7385
-
- Submitted-by: "Kim F. Storm" <storm@texas.dk>
- Posting-number: Volume 22, Issue 50
- Archive-name: nn6.4/part15
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then feed it
- # into a shell via "sh file" or similar. To overwrite existing files,
- # type "sh file -c".
- # The tool that generated this appeared in the comp.sources.unix newsgroup;
- # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
- # Contents: data.h doc/PROBLEMS expire.c save.c
- # Wrapped by rsalz@litchi.bbn.com on Fri May 11 12:42:44 1990
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- echo If this archive is complete, you will see the following message:
- echo ' "shar: End of archive."'
- if test -f 'data.h' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'data.h'\"
- else
- echo shar: Extracting \"'data.h'\" \(6540 characters\)
- sed "s/^X//" >'data.h' <<'END_OF_FILE'
- X/*
- X * (c) Copyright 1990, Kim Fabricius Storm. All rights reserved.
- X *
- X * Internal representation of the master, group, and article
- X * information read from the database.
- X *
- X * For each article read from the database, an article_header
- X * structure is initialized.
- X */
- X
- X/*
- X * The magic number allows us to change the format of the database
- X * and tell the users about it
- X */
- X
- X#define NNDB_MAGIC 0x4e4e2302 /* NN#2 */
- X
- X/*
- X * global master data
- X */
- X
- X#define DB_LOCK_MESSAGE 80
- X
- Xtypedef struct {
- X int32 db_magic;
- X char db_lock[DB_LOCK_MESSAGE];
- X time_t db_created; /* when database was last built */
- X time_t last_scan; /* age of active file at last scan */
- X off_t last_size; /* size of active file at last scan */
- X group_number number_of_groups;
- X int free_groups;
- X} master_header;
- X
- X/*
- X * group information
- X */
- X
- Xtypedef struct group_header {
- X
- X /* this part of the header is read from the MASTER file */
- X
- X flag_type master_flag; /* master's flags */
- X
- X# define M_VALID FLAG(1) /* group present in active file */
- X# define M_CONTROL FLAG(2) /* group is control group */
- X# define M_NO_DIRECTORY FLAG(3) /* group directory not found */
- X# define M_ALWAYS_DIGEST FLAG(4) /* D in GROUPS */
- X# define M_NEVER_DIGEST FLAG(5) /* N in GROUPS */
- X# define M_EXPIRE FLAG(6) /* expire in progress or pending */
- X# define M_BLOCKED FLAG(7) /* don't trust this entry */
- X# define M_MUST_CLEAN FLAG(8) /* group should be cleaned */
- X# define M_MODERATED FLAG(9) /* group is moderated */
- X# define M_ALIASED FLAG(10) /* =xxx in active file */
- X# define M_NOPOST FLAG(11) /* 'n' in active file */
- X# define M_AUTO_RECOLLECT FLAG(12) /* R in GROUPS */
- X# define M_AUTO_ARCHIVE FLAG(13) /* >file in GROUPS */
- X# define M_INCLUDE_OLD FLAG(14) /* O in GROUPS */
- X# define M_IGNORE_G FLAG(15) /* ignore this group (GROUPS X) */
- X# define M_IGNORE_A FLAG(16) /* ignore this group (ACTIVE x) */
- X# define M_IGNORE_GROUP (M_IGNORE_G | M_IGNORE_A)
- X
- X article_number first_db_article; /* min article in db */
- X article_number last_db_article; /* max article in db */
- X
- X article_number first_a_article; /* min article in active */
- X article_number last_a_article; /* max article in active */
- X
- X off_t index_write_offset;
- X off_t data_write_offset;
- X
- X time_t creation_time; /* when group was created */
- X
- X int group_name_length;
- X
- X /* this part is initialized during reading of the GROUPS file */
- X
- X /* DO NOT CHANGE THE POSITION OF group_flag AS THE FIRST FIELD */
- X /* AFTER THE PART WHICH IS SAVED IN THE MASTER FILE */
- X
- X flag_type group_flag; /* client's flags */
- X
- X# define G_UNSUBSCRIBED FLAG(1) /* ! in .newsrc */
- X# define G_READ FLAG(2) /* group has been visited */
- X# define G_FAKED FLAG(3) /* faked group - not in .newsrc */
- X# define G_NEW FLAG(5) /* new group */
- X# define G_FOLDER FLAG(6) /* "group" is a folder file */
- X# define G_DIRECTORY FLAG(7) /* "group" is directory */
- X# define G_SEQUENCE FLAG(8) /* in presentation sequence */
- X# define G_DONE FLAG(9) /* sequence is done with group */
- X# define G_MAILBOX FLAG(10) /* user's mail box file */
- X# define G_MERGE_HEAD FLAG(11) /* merged group head */
- X# define G_MERGE_SUB FLAG(12) /* merged group sub element */
- X# define G_MERGED (G_MERGE_HEAD | G_MERGE_SUB)
- X# define G_COUNTED FLAG(13) /* counted in unread_count */
- X
- X group_number group_num;
- X group_number preseq_index;
- X
- X char * group_name;
- X
- X char * archive_file;
- X
- X article_number first_article;
- X article_number last_article;
- X
- X article_number current_first; /* first article currently read in */
- X
- X struct group_header *next_group; /* group sequence */
- X struct group_header *prev_group;
- X
- X struct group_header *merge_with; /* merged groups */
- X
- X char *kill_list;
- X char *save_file; /* default save file from init */
- X char *enter_macro;
- X
- X struct group_header *newsrc_seq; /* .newsrc sequence */
- X char *newsrc_line; /* .newsrc line for this group */
- X char *newsrc_orig; /* original newsrc_line if changed */
- X char *select_line; /* .nn/select line for this group */
- X char *select_orig; /* original select_line if changed */
- X
- X int32 unread_count; /* number of unread articles */
- X} group_header;
- X
- X
- X/* size of the part of the group header placed on backing storage */
- X
- X
- X#define SAVED_GROUP_HEADER_SIZE(group) \
- X (((char *)(&((group).group_flag))) - ((char *)(&(group))))
- X
- X/*
- X * Internal article header information.
- X */
- X
- Xtypedef struct {
- X union {
- X article_number au_number; /* article number in the group */
- X char *au_string;
- X } au_union;
- X group_header *a_group; /* if merged article menu */
- X
- X /* indexes to header line text */
- X off_t hpos; /* first byte of header */
- X off_t fpos; /* first byte in article text */
- X off_t lpos; /* last pos of article */
- X
- X time_stamp t_stamp; /* encoded time_stamp */
- X time_stamp root_t_stamp; /* subject's time_stamp */
- X
- X char * sender; /* sender's name */
- X char * subject; /* subject (w/o Re:) */
- X
- X int16 replies; /* no of Re: */
- X int16 lines; /* no of lines */
- X
- X int8 subj_length; /* length of subject */
- X int8 name_length; /* length of sender */
- X
- X attr_type attr; /* attributes: */
- X attr_type disp_attr; /* currently displayed attr. */
- X /* warning: notice relation between A_SELECT and A_AUTO_SELECT */
- X
- X# define A_READ 0x01 /* article has been read */
- X# define A_SEEN 0x02 /* article presented on menu */
- X# define A_LEAVE 0x03 /* marked for later activity */
- X# define A_LEAVE_NEXT 0x04 /* marked for next invokation */
- X# define A_CANCEL 0x05 /* folder entry cancelled */
- X# define A_KILL 0x06 /* eliminate article */
- X# define A_SELECT 0x08 /* article has been selected */
- X# define A_AUTO_SELECT 0x09 /* will match & A_SELECT */
- X
- X# define A_NOT_DISPLAYED 0x7f /* not currently on menu */
- X
- X flag_type flag; /* attributes and flags: */
- X
- X# define A_SAME FLAG(1) /* same subject as prev. article */
- X# define A_ALMOST_SAME FLAG(2) /* A_SAME (match-limit) */
- X# define A_NEXT_SAME FLAG(4) /* next is same subject */
- X# define A_DIGEST FLAG(5) /* digest sub article */
- X# define A_FULL_DIGEST FLAG(6) /* full digest */
- X# define A_FOLDER FLAG(7) /* article file = "folder_path" */
- X# define A_FAKED FLAG(8) /* only 'number' is valid */
- X# define A_ST_FILED FLAG(10) /* articles is saved */
- X# define A_ST_REPLY FLAG(11) /* sent reply to article */
- X# define A_ST_FOLLOW FLAG(12) /* sent followup to article */
- X
- X} article_header;
- X
- X
- X#define a_number au_union.au_number
- X#define a_string au_union.au_string
- X
- END_OF_FILE
- if test 6540 -ne `wc -c <'data.h'`; then
- echo shar: \"'data.h'\" unpacked with wrong size!
- fi
- # end of 'data.h'
- fi
- if test -f 'doc/PROBLEMS' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'doc/PROBLEMS'\"
- else
- echo shar: Extracting \"'doc/PROBLEMS'\" \(13534 characters\)
- sed "s/^X//" >'doc/PROBLEMS' <<'END_OF_FILE'
- X KNOWN PROBLEMS
- X --------------
- X
- XHere is a collection of some of the problems people have had with
- Xinstallation and operation of nn in the past. Some of these may no
- Xlonger be completely valid since release 6.4 has changed quite a
- Xnumber of things since release 6.3, but there may still be some hints
- Xto solving the problems you might encounter.
- X
- X
- X RUNNING NN ON 80286
- X -------------------
- X
- XThe system and machine file for a '286 running Microport UNIX V/AT are
- Xs-uport2-2.h and m-i80286.h, but to get it running you probably have
- Xto do the following things as well:
- X
- X- tgetstr.o in libcurses is broken in 2.4.
- X Get an old version (e.g. 1.3) and replaced tgetstr.o.
- X
- X- Use the m286 malloc posted recently to comp.sources.misc
- X
- XThanks to Wietse Z. Venema and Miek Grenier for a lot of work on
- Xlocating and fixing 16/32 bit problems in nn 6.3 which caused it not
- Xto work on the '286. (I hope I haven't introduced new problems in
- X6.4, but I cannot promise it).
- X
- X NNTP PROBLEMS
- X -------------
- X
- XOther problems with the current NNTP support are described in the NNTP
- Xfile.
- X
- X
- X FILE PERMISSIONS
- X ----------------
- X
- XYou should run as root when installing the package & db, because some
- Xdirectories might be created in places where ordinary users are not
- Xallowed to write, and secondly because it is not allowed to change the
- Xowner of a file (nnmaster) on some systems.
- X
- XSpecifically, nnmaster will report "nnmaster already running" if it
- Xdoesn't have write access to the MASTER_DIRECTORY.
- X
- XThis might be the cause of your problems.
- X
- X
- XIn general, the permissions and ownership of the various programs
- Xshould be set to allow the following access:
- X
- X- To let the nnmaster READ the news directory (no problem since
- X /usr/spool/news normally has mode 755),
- X
- X- To let the nnmaster WRITE in the db-directory and files
- X
- X- To let ordinary users programs (i.e. nn) READ the db-directory.
- X
- XFor example,
- X owner group mode
- X db-directory & files: news news 755
- X nnmaster: news news 4755 (suid)
- X nn etc: storm other 755
- X
- X
- X EXPIRED ARTICLES STILL SHOW UP ON THE MENUS
- X -------------------------------------------
- X
- XThis happens if expiration has not been performed on the database
- Xafter expire has been run on the news system (or NNTP server).
- X
- XTo run expire on the database regularly, inserting the following
- Xcommand in the crontab to be executed at a suitable time (with
- Xpermissions of the owner of nnmaster). Preferably it should run
- Ximmediately after normal expire has *completed*:
- X /usr/local/bin/nnadmin =EYW
- X
- XThis expire is relatively cheap on a local system (< 10 minutes), but
- Xvia NNTP, it may be more expensive, so there it is recommended to only
- Xrun once a week or so. I have some directions for a patch to the NNTP
- Xserver which will cure this problem -- see NNTP and nntp.c!
- X
- X
- X ABOUT BLOCKED GROUPS
- X --------------------
- X
- XA group is blocked while the nnmaster is collecting new articles in
- Xthat group. In a newly initialized database, all groups will be
- Xblocked until the nnmaster has collected them the first time, which
- Xmay take an hour or so the first time you run nnmaster -r.
- X
- XThis means that there may not be any news to read for a while after
- Xyou have just started the nnmaster the first time.
- X
- XIf you run nnmaster with only a selection of groups on the command
- Xline, you may also see some blocked groups among the groups that are
- Xnot collected.
- X
- X
- X PROBLEMS SENDING MAIL
- X ---------------------
- X
- XSome people have experienced problems sending mail.
- X
- XThe unfortunate thing about this is that the report about the problems
- Xis sent to the user *by mail* - sort of catch-22.
- X
- XThe most common cause of these problems is that the definition of
- XREC_MAIL in config.h is not correct. Either it is an invalid path or
- Xprogram name, or the command does not read its standard input to
- Xget the letter (for example, MMDF's `post' command wants the letter in
- Xa file). The 'inst' script will check that the REC_MAIL and INEWS
- Xprograms exist, but not that they actually work.
- X
- XIn another case, uux was not silent causing the trace file (in the aux
- Xscript) to be non-empty which fooled nn to think that nothing had been
- Xsent (although it did). If you get failed reply/follow-up messages
- Xlook for a line saying something like "uucp job XXXXX" - This is
- Xcaused by the environment setting JOBNO=ON.
- X
- XIn one case, the recmail program was corrupted.
- X
- XAlso notice that some recmail programs may treat a line consisting of
- Xa single period in the first position as end-of-file.
- X
- XIf you don't have any program which can be used as recmail, there is
- Xone possible candidate in the contrib/ directory.
- X
- X
- X ACCESSING NEWS REMOTELY
- X -----------------------
- X
- XI have received the following problem description which seems to
- Xindicate a network problem. You can now set the variable
- X retry-on-error
- Xto the number of times nn should try to open an article (you may
- Xwant to do this in the global init file!):
- X
- X We are running on a VAXstation 2000, with the news accessed
- X remotely, so I get a lot of "can't read" errors. It seems to
- X me these errors should ALWAYS require acknowledgement before
- X clearing the message, and should offer the possibility to
- X re-try the operation (which usually then works for us).
- X
- XThere is a similar option [-y] to nnmaster which can be set to have
- Xthe nnmaster perform retries as well.
- X
- X
- X GROUPS BECOME OR REMAIN EMPTY
- X -----------------------------
- X
- XIt has been observed on a few occations that some groups are not
- Xcollected correctly. We have seldom managed to track down the actual
- Xproblem, but in practically all cases, NNTP and/or NFS has been
- Xinvolved, either accessing the news spool files remotely, or nnmaster
- Xupdating the database on another machine, or both.
- X
- XIt is very much recommended that nnmaster runs on the system when the
- Xdatabase is placed, and if at all possible that this is also the
- Xsystem on which the news spool files are located. Furthermore, this
- Xis by far the most efficient way to run things.
- X
- XIn any case, don't hesistate to tell me if you see this problem with
- Xrelease 6.4.
- X
- X
- X TERMINAL I-O PROBLEMS
- X ---------------------
- X
- Xnn does not echo the characters you type except when you are entering
- Xa string, e.g. a file name. Instead it attempts to make a significant
- Xchange to the data displayed on the screen. On a slow system this may
- Xbe seen as a drawback; on fast systems it is an intended feature!
- X
- XIf CBREAK is available, nn will use it, but when CBREAK is not avaiable
- Xnn uses raw mode when reading from the keyboard and cooked mode when
- Xprinting on the screen (it flips forth and back). [This behaviour can
- Xbe disabled by unsetting the flow-control variable] This has caused
- Xproblems on some systems (e.g. the 3B2) where the tty driver is
- Xlocated on a dedicated IO-processor, which for some reason handles
- Xioctl's "out of band". I have tried to work around these problems by
- Xoutputting \r\n sequences where \n should have been sufficient.
- X
- Xnn also intentionally discards type-ahead at certain points, but only
- Xif CBREAK mode is not supported, and the flow-control variable is set.
- X
- XOn some systems, TCSETAF also flushes the output queue; you may try to
- Xreplace it by TCSETAW followed by TCFLSH.
- X
- XOn terminals where the arrow keys send single characters, nn will not
- Xrecognize the arrow keys as arrows if they send a character which is
- Xalready recognized by nn for another purpose. For example, on the
- Xwyse 50, the left arrow key sends the same code as the backspace key
- Xwhich is the default erase-key. Therefore, the left arrow key will be
- Xinterpreted as the erase-key. (This is contrary to 6.3 where the
- Xnormal function of the erase-key was - erroneously - inhibited and
- Xinterpreted as the arrow key).
- X
- X
- X Cnews
- X -----
- X
- Xnn wants articles to contain Lines: headers, but Cnews doesn't
- Xgenerate these in the default setup. You may uncomment the
- XLines: code in the inews script.
- X
- XThe requirement for Cnews to update the 'min' field in the active
- Xfield has been removed in release 6.4.
- X
- X
- X NNMASTER WILL NOT START OR IS LOOPING
- X -------------------------------------
- X
- XIf no nnmaster is running, and nnmaster refuses to start up, you
- Xshould check for the existence of the MPID file in the LIB directory,
- XIf it exists, it should be removed.
- X
- XIf nnmaster starts looping, you should check the permissions on the
- XLIB directory and notice if a GATE file exists which nnmaster is not
- Xallowed to unlink.
- X
- X
- X NNMASTER DIES RANDOMLY
- X ----------------------
- X
- XThe definition DETATCH_TERMINAL in the s- file you use may not work
- X(it is a no-op on some systems). This will cause a hangup signal to
- Xbe sent to the master when you logout, and that will terminate the
- Xmaster. This may explain why the nnmaster seems to die for no
- Xapparant reason!
- X
- XIf nnmaster accesses news via NNTP, you should be aware of an NNTP
- Xlimitation which may cause problems to the nnmaster: Groups with more
- Xthan 4096 articles may cause the nntp server to crash! The easiest
- Xway to circumvent this problem is run expire on the large groups more
- Xfrequently. (Thanks to Scott Hankin for this information).
- X
- X
- X WARNINGS DURING COMPILATION
- X ---------------------------
- X
- XIf you get a syntax error when compiling the folder.c file, you
- Xprobably have defined HAVE_DIRECTORY in the s- file, but even though
- Xthe include file exists, it does not define the DIR type. Either get
- Xhold of a public domain directory package (look in the gnu
- Xdistribution), or just undefine HAVE_DIRECTORY which causes nn to use
- X(much slower) shell commands for file name completion (and disables
- Xthe ?-help for file names).
- X
- XIf the linker complains about not finding the function `strcspn'
- X(which should be in most standard libraries these days), define the
- Xsymbol STRCSPN in the m- file (or config.h) to use the version in
- Xregexp.c.
- X
- X
- X FORMATTING THE MANUALS
- X ----------------------
- X
- XMany versions of the -man package may have problems handling the `@'
- Xcharacters as hanging tags (.TP). Fix your man package by
- Xsubstituting ALL occurrences of the @ character in tmac.an (or perhaps
- Xtmac.an.new) by a BEL (^G) character.
- X
- X
- X RESIZING
- X --------
- X
- XResizing only works with termcap (on BSD systems)! It may also work
- Xon System V, but then you will probably also have to define the symbol
- XSYSV_RESIZING in config.h (see term.c).
- X
- XIf resizing occurs while reading an article, the article is repositioned
- Xon the first page of the article.
- X
- X
- X NNMASTER AND NN DOES NOT FIND ANY NEWS
- X --------------------------------------
- X
- XAll known occurrences of this problem have been identified and fixed,
- Xor somehow "explained" as being a NFS networking problem.
- X
- XBe careful about the 'limit' and 'old' variables. Setting them in the
- Xinit file may cause nn to behave strangely (as documented :-)
- X
- X
- X THE DATABASE BECOMES CORRUPTED FOR NO APPARENT REASON
- X -----------------------------------------------------
- X
- XThis has been seen on some systems in the past.
- X
- X- The expire problem in release 6.3 has been fixed, since the code
- X to perform the expiration has been rewritten and optimized. No
- X known bugs exist in this code.
- X
- X- Some .o files had not been recompiled by make after modifying the
- X config.h file (this happened on SunOS 4.0 which seems to forget to
- X update file modification times for some files (has anybody seen this
- X before?)).
- X
- X
- X 8 BIT SUPPORT
- X -------------
- X
- XI am rather embarrassed to admit that a program leaving Denmark in
- X1990 still does not fully support 8 bit character sets - however, that
- Xis the plain truth. Currently, all characters typed on the keyboard
- Xare stripped to 7 bits, but articles displayed on the screen can be
- Xshown either in 7 or 8 bit mode via the data-bits variable.
- X
- X
- X MAIL RECORDS
- X ------------
- X
- XThere are some things you have to consider in connection with the mail
- Xand news record files:
- X
- X - When you :mail to yourself, a copy is not saved in the mail-record.
- X
- X - If the mail/post fails, the message is saved in ~/dead.letter instead
- X
- X - Since the posting is performed in the background and it may take
- X upto a minute to complete posting an article, updating news-record
- X will not happen instantly.
- X
- X - In previous releases the mail header created by nn in the record
- X files are not recognized by the digest splitting code in nn, i.e.
- X they always appear to contain a single article. This was fixed in
- X release 6.3, but you may have some very old folders which nn will
- X not be able to split (there is no From: line).
- X
- X
- X OLD AWK vs. NEW AWK
- X -------------------
- X
- XSome scripts will not run with 'new awk'. If your system's `awk' is
- Xthe new awk, you must define the symbol OLD_AWK to the path name of
- Xthe old awk program (normally `oawk'). See s-template.h.
- X
- X
- X NN ON APOLLOS
- X -------------
- X
- XApollo's C-preprocessor may not make a working ymakefile, because it
- Xmay convert leading tabs to spaces, and thus confusing make. You'll
- Xhave to reconstruct the tabs somehow, e.g. using something like
- X sed 's/^ */TAB/' < ymakefile > ... (not tested!!)
- X
- XAlso notice that releases prior to SR10 will not handle directories
- Xwith more than 1300 entries, thus giving problems if you have more
- Xthan 650 groups in the database directory (d+x file). Leaving
- XDB_DIRECTORY undefined should fix this.
- X
- X
- X OTHER PROBLEMS
- X --------------
- X
- XThe 'master flags' set on a group with nnadmin are forgotten if the
- Xdatabase is reinitialized with nnmaster -I. To set these flags
- Xpermanently, you should set them in the GROUPS file.
- END_OF_FILE
- if test 13534 -ne `wc -c <'doc/PROBLEMS'`; then
- echo shar: \"'doc/PROBLEMS'\" unpacked with wrong size!
- fi
- # end of 'doc/PROBLEMS'
- fi
- if test -f 'expire.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'expire.c'\"
- else
- echo shar: Extracting \"'expire.c'\" \(14104 characters\)
- sed "s/^X//" >'expire.c' <<'END_OF_FILE'
- X/*
- X * (c) Copyright 1990, Kim Fabricius Storm. All rights reserved.
- X *
- X * Expire will remove all entries in the index and data files
- X * corresponding to the articles before the first article registered
- X * in the active file. No attempt is made to eliminate other
- X * expired articles.
- X */
- X
- X#include "config.h"
- X#include "db.h"
- X#include "dir.h"
- X
- Ximport int trace, debug_mode;
- Ximport int nntp_failed;
- X
- X/*
- X * Expire methods:
- X * 1: read directory and reuse database info.
- X * 2: "slide" index and datafiles (may leave unexpired art. in database)
- X * 3: recollect group to expire (also if "min" still exists)
- X */
- X
- Xexport expire_method = 1; /* expire method */
- Xexport recollect_method = 1; /* recollection method -- see do_expire */
- Xexport expire_level = 0; /* automatic expiration detection */
- X
- X#ifdef HAVE_DIRECTORY
- X
- Xstatic article_number *article_list = NULL;
- Xstatic long art_list_length = 0;
- X
- Xstatic sort_art_list(f1, f2)
- Xregister article_number *f1, *f2;
- X{
- X return (*f1 < *f2) ? -1 : (*f1 == *f2) ? 0 : 1;
- X}
- X
- Xstatic article_number *get_article_list(dir)
- Xchar *dir;
- X{
- X DIR *dirp;
- X register Direntry *dp;
- X register char c, *pp, *cp;
- X register article_number *art;
- X register long count = 0; /* No. of completions plus one */
- X
- X if ((dirp = opendir(dir)) == NULL)
- X return NULL; /* tough luck */
- X
- X art = article_list;
- X count = 0;
- X
- X while ((dp = readdir(dirp)) != NULL) {
- X cp = dp->d_name;
- X#ifdef FAKED_DIRECTORY
- X if (dp->d_ino == 0) continue;
- X cp[14] = NUL;
- X#endif
- X for (pp = cp; c = *pp++; )
- X if (!isascii(c) || !isdigit(c)) break;
- X if (c) continue;
- X
- X if (count == art_list_length) {
- X art_list_length += 250;
- X article_list = resizeobj(article_list, article_number, art_list_length + 1);
- X art = article_list + count;
- X }
- X *art++ = atol(cp);
- X count++;
- X }
- X closedir(dirp);
- X if (count)
- X quicksort(article_list, count, article_number, sort_art_list);
- X *art = 0;
- X
- X return article_list;
- X}
- X
- Xstatic long expire_in_database(gh)
- Xregister group_header *gh;
- X{
- X FILE *old, *data, *ix;
- X off_t old_max_offset;
- X register article_number *list;
- X#ifdef NNTP
- X extern article_number *nntp_get_article_list();
- X#endif
- X article_number old_last_article;
- X long count;
- X
- X if (gh->first_db_article > gh->last_db_article) return 0;
- X
- X if (!init_group(gh)) return 0;
- X
- X if (debug_mode == 1) {
- X printf("\t\tExp %s (%ld..%ld)\r",
- X gh->group_name, gh->first_db_article, gh->last_db_article);
- X fl;
- X }
- X
- X count = 0;
- X old = data = ix = NULL;
- X
- X /* get list of currently available articles in the group */
- X#ifdef NNTP
- X if (use_nntp)
- X list = nntp_get_article_list(gh);
- X else
- X#endif
- X list = get_article_list(".");
- X
- X if (list == NULL || *list == 0) {
- X#ifdef NNTP
- X if (nntp_failed == 2) {
- X log_entry('N', "NNTP server supports neither LISTGROUP nor XHDR");
- X sys_error("Cannot use specified expire method (NNTP limitations)");
- X }
- X if (nntp_failed) return -1;
- X#endif
- X if (debug_mode == 1) { printf("\rempty"); fl; }
- X /* group is empty - clean it */
- X count = gh->last_db_article - gh->first_db_article + 1;
- X clean_group(gh);
- X gh->last_db_article = gh->last_a_article;
- X gh->first_db_article = gh->last_db_article + 1;
- X db_write_group(gh);
- X return count;
- X }
- X
- X /*
- X * Clean & block the group while expire is working on it.
- X */
- X
- X gh->first_db_article = 0;
- X old_last_article = gh->last_db_article;
- X gh->last_db_article = 0;
- X
- X gh->index_write_offset = (off_t)0;
- X old_max_offset = gh->data_write_offset;
- X gh->data_write_offset = (off_t)0;
- X
- X gh->master_flag &= ~M_EXPIRE;
- X gh->master_flag |= M_BLOCKED;
- X
- X db_write_group(gh);
- X
- X /*
- X * We ignore the old index file, and we unlink the data file
- X * immediately after open because we want to write a new.
- X */
- X
- X (void)open_data_file(gh, 'x', -1);
- X old = open_data_file(gh, 'd', OPEN_READ | OPEN_UNLINK);
- X if (old == NULL) goto out;
- X
- X data = open_data_file(gh, 'd', OPEN_CREATE | MUST_EXIST);
- X ix = open_data_file(gh, 'x', OPEN_CREATE | MUST_EXIST);
- X
- X while (ftell(old) < old_max_offset) {
- X if (s_hangup) {
- X /* ok, this is what we got -- let collect get the rest */
- X old_last_article = gh->last_db_article;
- X break;
- X }
- X
- X /*
- X * maybe not enough articles, or last one is incomplete
- X * we take what there is, and leave the rest to do_collect()
- X * It may actually be ok if the last articles have been expired/
- X * cancelled!
- X */
- X
- X if (db_read_art(old) <= 0) {
- X break;
- X }
- X
- X if (debug_mode == 1) { printf("\r%ld", (long)db_hdr.dh_number); fl; }
- X
- X /* check whether we want this article */
- X
- X while (*list && db_hdr.dh_number > *list) {
- X /* potentially, we have a problem here: there might be an */
- X /* article in the directory which is not in the database! */
- X /* For the moment we just ignore this - it might be a bad */
- X /* article which has been rejected!! */
- X list++;
- X }
- X
- X if (*list == 0) {
- X /* no more articles in the directory - the rest must be */
- X /* expired. So we ignore the rest of the data file */
- X break;
- X }
- X
- X if (db_hdr.dh_number < *list) {
- X /* the current article from the data file isn't in the */
- X /* article list, so it must be expired! */
- X count++;
- X if (debug_mode == 1) { printf("\t%ld", count); fl; }
- X continue;
- X }
- X
- X if (gh->first_db_article == 0) {
- X gh->first_db_article = db_hdr.dh_number;
- X gh->last_db_article = db_hdr.dh_number - 1;
- X }
- X
- X if (gh->last_db_article < db_hdr.dh_number) {
- X gh->data_write_offset = ftell(data);
- X
- X /* must fill gab between last index and current article */
- X while (gh->last_db_article < db_hdr.dh_number) {
- X if (!db_write_offset(ix, &(gh->data_write_offset)))
- X write_error();
- X gh->last_db_article++;
- X }
- X }
- X
- X if (db_write_art(data) < 0) write_error();
- X }
- X
- X if (gh->first_db_article == 0) {
- X gh->first_db_article = old_last_article + 1;
- X gh->last_db_article = old_last_article;
- X } else {
- X gh->data_write_offset = ftell(data);
- X while (gh->last_db_article < old_last_article) {
- X /* must fill gab between last index and last article */
- X ++gh->last_db_article;
- X if (!db_write_offset(ix, &(gh->data_write_offset)))
- X write_error();
- X }
- X gh->index_write_offset = ftell(ix);
- X }
- X
- X gh->master_flag &= ~M_BLOCKED;
- X
- X db_write_group(gh);
- X
- X out:
- X if (old) fclose(old);
- X if (data) fclose(data);
- X if (ix) fclose(ix);
- X
- X if (debug_mode) putchar(NL);
- X
- X return count;
- X}
- X#else
- X#define expire_in_database expire_sliding
- X#endif
- X
- Xstatic long expire_sliding(gh)
- Xregister group_header *gh;
- X{
- X FILE *old_x, *old_d;
- X FILE *new;
- X off_t index_offset, data_offset, new_offset;
- X long count, expire_count;
- X char *err_message;
- X
- X#define expire_error(msg) { \
- X err_message = msg; \
- X goto error_handler; \
- X}
- X
- X old_x = old_d = new = NULL;
- X
- X#ifdef RENUMBER_DANGER
- X /*
- X * check whether new first article is collected
- X */
- X
- X if (!art_collected(gh, gh->first_a_article)) {
- X expire_count = gh->first_db_article - gh->last_db_article + 1;
- X err_message = NULL;
- X goto error_handler; /* renumbering, collect from start */
- X }
- X#else
- X if (gh->first_a_article <= gh->first_db_article)
- X return 0;
- X#endif
- X
- X expire_count = gh->first_a_article - gh->first_db_article;
- X
- X new = NULL;
- X
- X /*
- X * Open old files, unlink after open
- X */
- X
- X old_x = open_data_file(gh, 'x', OPEN_READ|OPEN_UNLINK);
- X old_d = open_data_file(gh, 'd', OPEN_READ|OPEN_UNLINK);
- X
- X if (old_x == NULL || old_d == NULL)
- X expire_error("INDEX or DATA file missing");
- X
- X /*
- X * Create new index file; copy from old
- X */
- X
- X new = open_data_file(gh, 'x', OPEN_CREATE);
- X if (new == NULL)
- X expire_error("INDEX: cannot create");
- X
- X /*
- X * index_offset is the offset into the old index file for the
- X * first entry in the new index file
- X */
- X
- X index_offset = get_index_offset(gh, gh->first_a_article);
- X
- X /*
- X * adjust the group's index write offset (the next free entry)
- X */
- X
- X gh->index_write_offset -= index_offset;
- X
- X /*
- X * calculate the number of entries to copy
- X */
- X
- X count = gh->index_write_offset / sizeof(off_t);
- X
- X /*
- X * data offset is the offset into the old data file for the
- X * first byte in the new data file; it is initialized in the
- X * loop below, by reading the entry in the old index file at
- X * offset 'index_offset'.
- X */
- X
- X data_offset = (off_t)0;
- X
- X /*
- X * read 'count' entries from the old index file starting from
- X * index_offset, subtract the 'data_offset', and output the
- X * new offset to the new index file.
- X */
- X
- X fseek(old_x, index_offset, 0);
- X
- X while (--count >= 0) {
- X if (!db_read_offset(old_x, &new_offset))
- X expire_error("INDEX: too short");
- X
- X if (data_offset == (off_t)0) data_offset = new_offset;
- X
- X new_offset -= data_offset;
- X if (!db_write_offset(new, &new_offset))
- X expire_error("NEW INDEX: cannot write");
- X }
- X
- X fclose(new);
- X fclose(old_x); old_x = NULL;
- X
- X /*
- X * copy from old data file to new data file
- X */
- X
- X new = open_data_file(gh, 'd', OPEN_CREATE);
- X if (new == NULL)
- X expire_error("DATA: cannot create");
- X
- X /*
- X * calculate offset for next free entry in the new data file
- X */
- X
- X gh->data_write_offset -= data_offset;
- X
- X /*
- X * calculate number of bytes to copy (piece of cake)
- X */
- X
- X count = gh->data_write_offset;
- X
- X /*
- X * copy 'count' bytes from the old data file, starting at offset
- X * 'data_offset', to the new data file
- X */
- X
- X fseek(old_d, data_offset, 0);
- X while (count > 0) {
- X char block[1024];
- X int count1;
- X
- X count1 = fread(block, sizeof(char), 1024, old_d);
- X if (count1 <= 0)
- X expire_error("DATA: read error");
- X
- X if (fwrite(block, sizeof(char), count1, new) != count1)
- X expire_error("DATA: write error");
- X
- X count -= count1;
- X }
- X
- X fclose(new);
- X fclose(old_d);
- X
- X /*
- X * Update group entry
- X */
- X
- X gh->first_db_article = gh->first_a_article;
- X
- X /*
- X * Return number of expired articles
- X */
- X
- X return expire_count;
- X
- X /*
- X * Errors end up here.
- X * We simply recollect the whole group once more.
- X */
- X
- Xerror_handler:
- X
- X if (new) fclose(new);
- X if (old_x) fclose(old_x);
- X if (old_d) fclose(old_d);
- X
- X if (err_message)
- X log_entry('E', "Expire Error (%s): %s", gh->group_name, err_message);
- X
- X clean_group(gh);
- X
- X /* will be saved & unblocked later */
- X
- X /*
- X * We cannot say whether any articles actually had to be expired,
- X * but then we must guess...
- X */
- X
- X return expire_count;
- X}
- X
- Xstatic block_group(gh)
- Xregister group_header *gh;
- X{
- X if ((gh->master_flag & M_BLOCKED) == 0) {
- X gh->master_flag |= M_BLOCKED;
- X db_write_group(gh);
- X }
- X}
- X
- Xstatic unblock_group(gh)
- Xregister group_header *gh;
- X{
- X if (gh->master_flag & M_BLOCKED) {
- X gh->master_flag &= ~(M_BLOCKED | M_EXPIRE);
- X db_write_group(gh);
- X }
- X}
- X
- Xdo_expire()
- X{
- X register group_header *gh;
- X long exp_article_count, temp;
- X int exp_group_count, must_expire;
- X time_t start_time;
- X
- X must_expire = 0;
- X
- X Loop_Groups_Header(gh) {
- X if (s_hangup) break;
- X if (gh->master_flag & M_IGNORE_GROUP) continue;
- X
- X if ((gh->master_flag & M_VALID) == 0) {
- X if (gh->last_db_article == 0) continue;
- X log_entry('X', "Group %s removed", gh->group_name);
- X gh->master_flag |= M_IGNORE_A;
- X clean_group(gh);
- X continue;
- X }
- X
- X#ifdef RENUMBER_DANGER
- X if (gh->last_db_article > gh->last_a_article ||
- X gh->first_db_article > gh->first_a_article) {
- X log_entry('X', "group %s renumbered", gh->group_name);
- X clean_group(gh);
- X continue;
- X }
- X#endif
- X
- X if (gh->master_flag & M_AUTO_RECOLLECT) {
- X switch (recollect_method) {
- X case 1: /* expire when new articles arrive */
- X if (gh->last_a_article <= gh->last_db_article) break;
- X case 2: /* expire unconditionally */
- X gh->master_flag |= M_EXPIRE;
- X must_expire = 1;
- X continue;
- X
- X case 3: /* clean when new articles arrive */
- X if (gh->last_a_article <= gh->last_db_article) break;
- X case 4: /* clean unconditionally */
- X gh->master_flag |= M_MUST_CLEAN;
- X continue;
- X default: /* ignore auto-recollect */
- X break;
- X }
- X }
- X
- X if (gh->index_write_offset > 0) {
- X if (gh->first_a_article > gh->last_db_article) {
- X if (trace)
- X log_entry('T', "%s expire void", gh->group_name);
- X if (debug_mode)
- X printf("%s expire void\n", gh->group_name);
- X clean_group(gh);
- X continue;
- X }
- X }
- X
- X if (gh->master_flag & M_EXPIRE) {
- X must_expire = 1;
- X continue;
- X }
- X
- X if (expire_level > 0 &&
- X (gh->first_db_article + expire_level) <= gh->first_a_article) {
- X if (trace)
- X log_entry('T', "%s expire level", gh->group_name);
- X if (debug_mode)
- X printf("Expire level: %s\n", gh->group_name);
- X gh->master_flag |= M_EXPIRE;
- X must_expire = 1;
- X continue;
- X }
- X }
- X
- X if (!must_expire) return 1;
- X
- X start_time = cur_time();
- X exp_article_count = exp_group_count = 0;
- X temp = 0;
- X
- X Loop_Groups_Header(gh) {
- X if (s_hangup) {
- X temp = -1;
- X break;
- X }
- X
- X if (gh->master_flag & M_IGNORE_GROUP) continue;
- X if ((gh->master_flag & M_EXPIRE) == 0) continue;
- X if (gh->master_flag & M_MUST_CLEAN) continue;
- X
- X if (trace)
- X log_entry('T', "Exp %s (%ld -> %ld)", gh->group_name,
- X (long)gh->first_db_article, (long)gh->first_a_article);
- X
- X switch (expire_method) {
- X case 1:
- X block_group(gh);
- X temp = expire_in_database(gh);
- X unblock_group(gh);
- X break;
- X
- X case 2:
- X block_group(gh);
- X temp = expire_sliding(gh);
- X unblock_group(gh);
- X break;
- X
- X case 4:
- X temp = gh->first_a_article - gh->first_db_article;
- X if (temp <= 0) break;
- X case 3:
- X gh->master_flag |= M_MUST_CLEAN;
- X break;
- X
- X }
- X
- X#ifdef NNTP
- X if (nntp_failed) {
- X /* connection broken while reading article list */
- X /* so no harm was done - leave the group to be expired */
- X /* again on next expire */
- X break;
- X }
- X#endif
- X
- X if (temp > 0) {
- X exp_article_count += temp;
- X exp_group_count++;
- X }
- X }
- X
- X if (exp_article_count > 0)
- X log_entry('X', "Expire: %ld art, %d gr, %ld s",
- X exp_article_count, exp_group_count,
- X (long)(cur_time() - start_time));
- X
- X return temp >= 0;
- X}
- END_OF_FILE
- if test 14104 -ne `wc -c <'expire.c'`; then
- echo shar: \"'expire.c'\" unpacked with wrong size!
- fi
- # end of 'expire.c'
- fi
- if test -f 'save.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'save.c'\"
- else
- echo shar: Extracting \"'save.c'\" \(16732 characters\)
- sed "s/^X//" >'save.c' <<'END_OF_FILE'
- X/*
- X * (c) Copyright 1990, Kim Fabricius Storm. All rights reserved.
- X *
- X * Save (or print) articles
- X */
- X
- X#include <signal.h>
- X#include <errno.h>
- X#include "config.h"
- X#include "term.h"
- X#include "keymap.h"
- X#include "news.h"
- X
- Xexport char *default_save_file = "+$F";
- Xexport int suggest_save_file = 1;
- Xexport char *unshar_header_file = "Unshar.Headers";
- Xexport int use_mail_folders = 0;
- Xexport int use_mmdf_folders = 0;
- Xexport int save_report = 1;
- Xexport int quick_save = 0;
- Xexport int conf_append = 0;
- Xexport int conf_create = 1;
- X
- Xexport char *save_counter_format = "%d"; /* format of save counter */
- Xexport int save_counter_offset = 0;
- X
- Xexport char printer[FILENAME] = DEFAULT_PRINTER;
- Xexport int edit_print_command = 1;
- X
- Xexport char patch_command[FILENAME] = "patch -p0";
- Xexport int edit_patch_command = 1;
- X
- Xexport char unshar_command[FILENAME] = SHELL;
- Xexport int edit_unshar_command = 0;
- X
- Xextern int file_completion();
- X
- Ximport char *temp_file;
- Ximport int shell_restrictions;
- Ximport char delayed_msg[];
- X
- Ximport int rot13_active;
- X
- Xstatic int save_mode;
- Xstatic char *unshar_cmd;
- X
- X#define HEADER_HANDLING 0x0f /* what should we do with the header */
- X
- X#define NO_HEADER 0 /* save without a header */
- X#define FULL_HEADER 1 /* save with full header */
- X#define SHORT_HEADER 2 /* save with partial header */
- X#define SHORT_HEADER_DG 3 /* save with partial header (digest) */
- X
- X
- X#define SEPARATE_FILES 0x0100 /* save as separate files */
- X#define UNIQUE_FILES 0x0200 /* save in unique files */
- X#define FILE_IS_NEW 0x0400 /* this is a new file */
- X#define APPEND_ARTNUM 0x0800 /* append article number to file name */
- X#define IS_PIPE 0x1000 /* output is on pipe */
- X#define DO_UNSHAR 0x2000 /* unshar article (or patch) */
- X#define DO_PATCH 0x4000 /* patch article */
- X#define DO_DECODE 0x8000 /* uudecode article */
- X
- X/* open modes for open_news_article for the various HEADER_HANDLINGs */
- X
- Xstatic int open_mode[] = {
- X SKIP_HEADER,
- X 0,
- X FILL_NEWS_HEADER | SKIP_HEADER,
- X FILL_DIGEST_HEADER | SKIP_HEADER
- X};
- X
- Xstatic FILE *save_file; /* output stream for saved files */
- Xstatic char *save_name; /* save file name */
- X
- Xstatic int uniq_counter; /* separate files counter */
- Xstatic char uniq_format[FILENAME]; /* sprintf format for '*' expansion */
- X
- Xstatic char last_dir[FILENAME] = "";
- X
- X/*
- X * return pointer to first path name component, that does not exist
- X */
- X
- Xstatic char *ckdir_path(name)
- Xchar *name;
- X{
- X char *slash;
- X char *component;
- X
- X component = name;
- X
- X while (slash = strchr(component, '/')) {
- X if (slash == component) {
- X /* ...//... */
- X component++;
- X continue;
- X }
- X *slash = NUL;
- X if (file_exist(name, "drx")) {
- X *slash++ = '/';
- X component = slash;
- X continue;
- X }
- X if (file_exist(name, (char *)NULL)) {
- X *slash = '/';
- X return NULL;
- X }
- X *slash = '/';
- X break;
- X }
- X return component;
- X}
- X
- X/*
- X * create directories in path name, starting from start
- X */
- X
- Xstatic mkdirs_in_path(name, start)
- Xchar *name, *start;
- X{
- X char *slash;
- X char *component;
- X
- X component = start;
- X
- X while (slash = strchr(component, '/')) {
- X if (slash == component) {
- X /* ...//... */
- X component++;
- X continue;
- X }
- X *slash = NUL;
- X if (mkdir(name, 0755)) {
- X msg("Cannot make %s/", name);
- X *slash = '/';
- X return 0;
- X }
- X *slash++ = '/';
- X component = slash;
- X }
- X return 1;
- X}
- X
- Xchar *init_save(command, mode_textp)
- Xchar command;
- Xchar **mode_textp;
- X{
- X char *mode_text;
- X static char last_input[FILENAME] = "";
- X static char name_buf[512]; /* buffer for file name & command expansion */
- X char *start, *last, *np;
- X char *ckdir_path();
- X
- X uniq_counter = 0;
- X
- X switch (command) {
- X
- X case K_SAVE_FULL_HEADER:
- X save_mode = FULL_HEADER;
- X mode_text = "Save";
- X goto cont_save;
- X
- X case K_SAVE_SHORT_HEADER:
- X save_mode = SHORT_HEADER;
- X mode_text = "Output";
- X goto cont_save;
- X
- X case K_SAVE_NO_HEADER:
- X save_mode = NO_HEADER;
- X mode_text = "Write";
- X
- X cont_save:
- X if (quick_save && (current_group->group_flag & G_FOLDER) == 0) {
- X if (current_group->save_file)
- X save_name = current_group->save_file;
- X else
- X save_name = default_save_file;
- X strcpy(last_input, save_name);
- X save_name = last_input;
- X } else {
- X prompt("\1%s on\1 (+~|) ", mode_text);
- X
- X save_name = current_group->save_file;
- X if (save_name == NULL && suggest_save_file)
- X save_name = default_save_file;
- X if (save_name != NULL) {
- X if (!expand_file_name(name_buf, save_name, 2)) return NULL;
- X save_name = name_buf;
- X }
- X
- X save_name = get_s(last_input, save_name, NONE, file_completion);
- X if (save_name == NULL || *save_name == NUL) return NULL;
- X
- X if (save_name[1] == NUL && save_name[0] == '+')
- X save_name = default_save_file;
- X else
- X if (current_group->save_file == NULL ||
- X strcmp(save_name, current_group->save_file))
- X strcpy(last_input, save_name);
- X }
- X
- X if (*save_name == '|') {
- X if (shell_restrictions) {
- X msg("Restricted operation - pipes not allowed");
- X return NULL;
- X }
- X mode_text = "Pipe";
- X save_name++;
- X save_mode |= IS_PIPE;
- X if (*save_name == '|') {
- X save_mode |= SEPARATE_FILES;
- X save_name++;
- X }
- X }
- X
- X if (!expand_file_name(name_buf, save_name, (save_mode & IS_PIPE) ? 11 : 3))
- X return NULL;
- X save_name = name_buf;
- X
- X if ((save_mode & IS_PIPE) == 0) {
- X np = strrchr(save_name, '*');
- X if (np != NULL) {
- X *np = NUL;
- X sprintf(uniq_format, "%s%s%s",
- X save_name, save_counter_format, np + 1);
- X *np = '*';
- X save_mode |= SEPARATE_FILES | UNIQUE_FILES;
- X } else {
- X np = strrchr(save_name, '$');
- X if (np != NULL && np[1] == 'N') {
- X if (current_group->group_flag & G_FOLDER) {
- X msg("$N is not defined in a folder");
- X return NULL;
- X }
- X *np = NUL;
- X sprintf(uniq_format, "%s%%ld%s", save_name, np + 2);
- X *np = '$';
- X save_mode |= SEPARATE_FILES | APPEND_ARTNUM;
- X }
- X }
- X }
- X break;
- X
- X case K_UUDECODE:
- X save_mode = NO_HEADER | DO_UNSHAR | DO_DECODE;
- X mode_text = "Decode";
- X goto unshar1;
- X
- X case K_PATCH:
- X save_mode = NO_HEADER | SEPARATE_FILES | DO_UNSHAR | DO_PATCH;
- X mode_text = "Patch";
- X if (!shell_restrictions && edit_patch_command) {
- X prompt("\1Patch command:\1 ");
- X save_name = get_s(NONE, patch_command, NONE, NULL_FCT);
- X if (save_name == NULL || *save_name == NUL) return NULL;
- X strcpy(patch_command, save_name);
- X }
- X unshar_cmd = patch_command;
- X goto unshar1;
- X
- X case K_UNSHAR:
- X save_mode = NO_HEADER | SEPARATE_FILES | DO_UNSHAR;
- X mode_text = "Unshar";
- X if (!shell_restrictions && edit_unshar_command) {
- X prompt("\1Unshar command:\1 ");
- X save_name = get_s(NONE, unshar_command, NONE, NULL_FCT);
- X if (save_name == NULL || *save_name == NUL) return NULL;
- X strcpy(unshar_command, save_name);
- X }
- X unshar_cmd = unshar_command;
- X
- X unshar1:
- X prompt("\1%s Directory:\1 ", mode_text);
- X save_name = current_group->save_file;
- X if (save_name != NULL) {
- X if (!expand_file_name(name_buf, save_name, 10)) return NULL;
- X save_name = name_buf;
- X }
- X
- X save_name = get_s(last_dir, save_name, NONE, file_completion);
- X if (save_name == NULL || *save_name == NUL) return NULL;
- X strcpy(last_dir, save_name);
- X if (!expand_file_name(name_buf, save_name, 1))
- X return NULL;
- X save_name = name_buf;
- X break;
- X
- X case K_PRINT:
- X save_mode = SHORT_HEADER | IS_PIPE;
- X
- X if (!shell_restrictions && edit_print_command) {
- X prompt("\1Print command:\1 ");
- X save_name = get_s(NONE, printer, NONE, NULL_FCT);
- X if (save_name == NULL || *save_name == NUL) return NULL;
- X strcpy(printer, save_name);
- X }
- X if (!expand_file_name(name_buf, printer, 1))
- X return NULL;
- X save_name = name_buf;
- X mode_text = "Print";
- X break;
- X
- X default:
- X msg("Illegal save command: %d", command);
- X return NULL;
- X }
- X
- X if (save_name == NULL) return NULL;
- X
- X if (!(save_mode & IS_PIPE)) {
- X if (file_exist(save_name, (save_mode & DO_UNSHAR) ? "wd" : "wf")) {
- X if (save_mode & DO_UNSHAR) {
- X int len = strlen(save_name);
- X if (save_name[len - 1] != '/')
- X strcpy(save_name + len, "/");
- X } else
- X if (conf_append) {
- X printf("\rAppend to: %s ? ", save_name);
- X clrline();
- X if (!yes(0)) return NULL;
- X }
- X } else {
- X if (errno != ENOENT) {
- X msg("Cannot access %s", save_name);
- X return NULL;
- X }
- X
- X if (save_mode & DO_UNSHAR) {
- X int len = strlen(save_name);
- X if (save_name[len - 1] != '/')
- X strcpy(save_name + len, "/");
- X }
- X
- X start = ckdir_path(save_name);
- X if (start == NULL) {
- X msg("No permission");
- X return NULL;
- X }
- X
- X last = strrchr(start, '/');
- X /* last != NULL => non-existing directories */
- X
- X if (conf_create && (!(save_mode & SEPARATE_FILES) || last)) {
- X printf("\rCreate ");
- X for (np = save_name; *np; np++) {
- X if (np == start) putchar('\"');
- X putchar(*np);
- X if ((save_mode & SEPARATE_FILES) && np == last) break;
- X }
- X printf("\" ?");
- X clrline();
- X if (yes(last != NULL) <= 0) return NULL;
- X }
- X
- X if (last && !mkdirs_in_path(save_name, start))
- X return NULL;
- X }
- X }
- X
- X if (mode_textp) *mode_textp = mode_text;
- X
- X save_mode |= FILE_IS_NEW; /* so save() will open it */
- X
- X if (save_mode & DO_DECODE) {
- X uud_start(save_name);
- X save_mode &= ~DO_UNSHAR;
- X }
- X
- X return save_name;
- X}
- X
- X
- Xsave(ah)
- Xarticle_header *ah;
- X{
- X register FILE *art;
- X register c, lcount, mode;
- X news_header_buffer hdrbuf;
- X int was_raw = 0, set_visual = 0;
- X char copybuf[FILENAME * 4], uniqbuf[FILENAME];
- X
- X if (ah->a_group) init_group(ah->a_group);
- X
- X mode = save_mode & HEADER_HANDLING;
- X if (mode == SHORT_HEADER && ah->flag & A_DIGEST)
- X mode = SHORT_HEADER_DG;
- X
- X art = open_news_article(ah, open_mode[mode], hdrbuf, (char *)NULL);
- X if (art == NULL) {
- X msg("Cannot read %s", group_path_name);
- X return 0;
- X }
- X
- X if (save_mode & DO_DECODE) {
- X save_file = NULL;
- X c = uudecode(ah, art);
- X fclose(art);
- X return (c < 0) ? 0 : 1;
- X }
- X
- X if (save_mode & UNIQUE_FILES) {
- X uniqbuf[0] = NUL;
- X do {
- X strcpy(copybuf, uniqbuf);
- X uniq_counter++;
- X sprintf(uniqbuf, uniq_format, uniq_counter + save_counter_offset);
- X if (strcmp(uniqbuf, copybuf) == 0) {
- X msg("save-counter \"%s\" does not generate unique file names",
- X save_counter_format);
- X goto fatal;
- X }
- X } while (file_exist(uniqbuf, (char *)NULL));
- X save_name = uniqbuf;
- X save_mode |= FILE_IS_NEW;
- X } else
- X if (save_mode & APPEND_ARTNUM) {
- X sprintf(uniqbuf, uniq_format, (long)(ah->a_number));
- X save_name = uniqbuf;
- X }
- X
- X if (save_mode & FILE_IS_NEW) {
- X if (save_mode & (IS_PIPE | DO_UNSHAR)) {
- X set_visual = 1;
- X was_raw = visual_off();
- X }
- X
- X if (save_mode & IS_PIPE) {
- X if ((save_file = popen(save_name, "w")) == NULL) {
- X msg("Cannot pipe to %s", save_name);
- X goto fatal;
- X }
- X } else
- X if (save_mode & DO_UNSHAR) {
- X if ((save_mode & DO_PATCH) == 0) {
- X if (!unshar_position(art))
- X goto fatal;
- X if (unshar_header_file)
- X store_header(ah, art, save_name, unshar_header_file);
- X }
- X
- X new_temp_file();
- X sprintf(copybuf,
- X "cd %s && { %s 2>&1 ; } | tee %s ; cat %s >> %s.Result ; rm %s",
- X save_name != NULL ? save_name : ".", unshar_cmd,
- X temp_file, temp_file,
- X (save_mode & DO_PATCH) ? "Patch" : "Unshar",
- X temp_file);
- X
- X save_file = popen(copybuf, "w");
- X if (save_file == NULL) {
- X msg("Cannot exec: '%s'", copybuf);
- X goto fatal;
- X }
- X printf("\r\n%s %s\r\n",
- X save_mode & DO_PATCH ? "PATCHING FROM" : "UNPACKING",
- X ah->subject ? ah->subject : "ARTICLE"); fl;
- X } else {
- X if ((save_file = open_file(save_name, OPEN_APPEND)) == NULL) {
- X msg("Cannot write %s", save_name);
- X fclose(art);
- X return 0;
- X }
- X if (ftell(save_file) != (off_t)0)
- X save_mode &= ~FILE_IS_NEW;
- X }
- X }
- X
- X clrline();
- X s_pipe = 0;
- X
- X if (mode != NO_HEADER)
- X mailbox_format(save_file, 1);
- X
- X if (mode == FULL_HEADER) {
- X off_t cnt = ah->fpos - ah->hpos;
- X while (--cnt >= 0) {
- X if ((c = getc(art)) == EOF) break;
- X putc(c, save_file);
- X }
- X } else
- X if (mode == SHORT_HEADER) {
- X if (news.ng_from)
- X fprintf(save_file, "From: %s\n", news.ng_from);
- X if (news.ng_date)
- X fprintf(save_file, "Date: %s\n", news.ng_date);
- X if (news.ng_subj)
- X fprintf(save_file, "Subject: %s\n", news.ng_subj);
- X fputc(NL, save_file);
- X } else
- X if (mode == SHORT_HEADER_DG) {
- X if (digest.dg_from)
- X fprintf(save_file, "From: %s\n", digest.dg_from);
- X if (digest.dg_date)
- X fprintf(save_file, "Date: %s\n", digest.dg_date);
- X if (digest.dg_subj)
- X fprintf(save_file, "Subject: %s\n", digest.dg_subj);
- X fputc(NL, save_file);
- X }
- X
- X fflush(save_file);
- X if (s_pipe) goto broken_pipe;
- X
- X mode = mode != NO_HEADER && (save_mode & (IS_PIPE | DO_UNSHAR)) == 0;
- X
- X lcount = 0;
- X while (ftell(art) < ah->lpos && fgets(copybuf, 512, art)) {
- X lcount++;
- X if (rot13_active) rot13_line(copybuf);
- X if (mode && is_header_line(copybuf))
- X fputc('~', save_file);
- X fputs(copybuf, save_file);
- X if (s_pipe) goto broken_pipe;
- X }
- X
- X if (mode != NO_HEADER)
- X lcount += mailbox_format(save_file, 0);
- X
- Xbroken_pipe:
- X fclose(art);
- X
- X if (save_mode & DO_UNSHAR) {
- X if ((c = pclose(save_file)) != 0)
- X sprintf(delayed_msg, "Save command failed; exit = %d", c);
- X save_file = NULL;
- X } else {
- X if (s_pipe)
- X msg("Command did not read complete article!");
- X else
- X if (save_report)
- X msg((save_mode & IS_PIPE) ? "%s: %d lines piped" :
- X (save_mode & FILE_IS_NEW) ? "%s created: %d lines written" :
- X "%s: %d lines appended", save_name, lcount);
- X
- X if (s_pipe || (save_mode & SEPARATE_FILES)) {
- X if (end_save() == 0) return 0;
- X } else
- X save_mode &= ~FILE_IS_NEW;
- X }
- X
- X#ifdef MAIL_READING
- X if (mail_auto_delete && (save_mode & IS_PIPE) == 0)
- X if (current_group->group_flag & G_MAILBOX)
- X if (ah->attr != A_CANCEL)
- X fcancel(ah);
- X#endif
- X if (set_visual) visual_on();
- X if (was_raw) raw();
- X
- X ah->flag |= A_ST_FILED;
- X
- X return !s_pipe || (save_mode & SEPARATE_FILES);
- X
- X fatal:
- X if (set_visual) visual_on();
- X if (was_raw) raw();
- X fclose(art);
- X return 0;
- X}
- X
- X
- Xend_save()
- X{
- X int c;
- X FILE *sf;
- X sf = save_file;
- X save_file = NULL;
- X
- X if (sf) {
- X if (save_mode & (IS_PIPE | DO_UNSHAR)) {
- X if ((c = pclose(sf)) != 0) {
- X if (c >= 256) c >>= 8; /* HACK */
- X sprintf(delayed_msg, "Save command failed; exit = %d", c);
- X return 0;
- X }
- X } else
- X fclose(sf);
- X }
- X
- X if (save_mode & DO_DECODE) {
- X uud_end();
- X }
- X if (save_mode & DO_UNSHAR)
- X sprintf(delayed_msg, "Output is saved in %s/%s.Result",
- X save_name != NULL ? save_name : ".",
- X (save_mode & DO_PATCH) ? "Patch" : "Unshar");
- X return 1;
- X}
- X
- Xstore_header(ah, f, dir, file)
- Xarticle_header *ah;
- XFILE *f;
- Xchar *dir, *file;
- X{
- X register int c;
- X off_t endpos;
- X FILE *h;
- X
- X if (dir != (char *)NULL && file[0] != '/')
- X file = relative(dir, file);
- X if ((h = open_file(file, OPEN_APPEND)) == NULL) {
- X msg("Cannot open %s", file);
- X return;
- X }
- X fseek(h, (off_t)0, 2);
- X if (!use_mmdf_folders && ftell(h) > 0) putc(NL, h); /* just in case */
- X mailbox_format(h, 1);
- X endpos = ftell(f) - ah->hpos;
- X fseek(f, ah->hpos, 0);
- X while (--endpos >= 0 && (c = getc(f)) != EOF)
- X putc(c, h);
- X
- X mailbox_format(h, 0);
- X fclose(h);
- X}
- X
- Xmailbox_format(f, top)
- XFILE *f;
- Xint top;
- X{
- X time_t now;
- X char *ctime();
- X
- X if (use_mmdf_folders) {
- X fprintf(f, "\001\001\001\001\n");
- X return 0;
- X }
- X
- X if (top == 0) {
- X fputc(NL, f);
- X return 1;
- X }
- X
- X if (use_mail_folders) {
- X now = cur_time();
- X fprintf(f, "From %s %s",
- X current_group->group_name, ctime(&now));
- X return 1;
- X }
- X
- X return 0;
- X}
- X
- Xchar *run_mkdir(dir, name_buf)
- Xchar *dir, *name_buf;
- X{
- X if (dir == NULL) {
- X prompt("\1Directory: \1");
- X dir = get_s(last_dir, NONE, NONE, file_completion);
- X if (dir == NULL || *dir == NUL) return NULL;
- X strcpy(last_dir, dir);
- X }
- X
- X if (*dir == '+' || *dir == '~') {
- X if (!expand_file_name(name_buf, dir, 1))
- X return NULL;
- X dir = name_buf;
- X }
- X
- X if (file_exist(dir, (char *)NULL)) {
- X msg("Directory %s already exists", dir);
- X return NULL;
- X }
- X
- X if (mkdir(dir, 0755)) {
- X msg("Cannot make %s", dir);
- X return NULL;
- X }
- X
- X return dir;
- X}
- X
- X
- Xchange_dir(dir, in_init)
- Xchar *dir;
- Xint in_init;
- X{
- X char dir_buf[FILENAME];
- X
- X if (dir == NULL) {
- X prompt("\1Directory: \1");
- X dir = get_s(last_dir, NONE, NONE, file_completion);
- X }
- X
- X if (dir == NULL || *dir == NUL) return 0;
- X
- X strcpy(last_dir, dir);
- X
- X if (*dir == '+' || *dir == '~') {
- X if (!expand_file_name(dir_buf, dir, 1)) return in_init;
- X dir = dir_buf;
- X }
- X
- X if (chdir(dir) == 0) {
- X if (!in_init) msg("Directory: %s", dir);
- X return 0;
- X }
- X
- X if (in_init) return 1;
- X
- X if (errno == EACCES)
- X msg("Cannot access directory %s", dir);
- X else {
- X /* should ask and create directory here */
- X msg("Directory not found: %s", dir);
- X }
- X
- X return 0;
- X}
- X
- X
- END_OF_FILE
- if test 16732 -ne `wc -c <'save.c'`; then
- echo shar: \"'save.c'\" unpacked with wrong size!
- fi
- # end of 'save.c'
- fi
- echo shar: End of archive 15 \(of 22\).
- cp /dev/null ark15isdone
- MISSING=""
- for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked all 22 archives.
- rm -f ark[1-9]isdone ark[1-9][0-9]isdone
- else
- echo You still must unpack the following archives:
- echo " " ${MISSING}
- fi
- exit 0
-